package com.cy.web.aspect;

import com.cy.dao.NoticeDao;
import com.cy.pojo.NoticeLog;
import com.cy.service.LogService;
import com.cy.web.common.annotation.RequiredLog;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Slf4j
@Aspect
@Component
public class NoticeLogAspect {

    @Autowired
    private LogService logService;

    /**
     * @Pointcut 注解用于定义切入点
     * @annotation(注解)为切入点表达式，后续由此注解描述的方法为切入
     * 点方法
     */
    @Pointcut("@annotation(com.cy.web.common.annotation.RequiredLog)")
    public void doLog(){}//此方法只负责承载切入点的定义

    @Around("doLog()")
    public Object doAround(ProceedingJoinPoint jp)throws Throwable{
        long t1=System.currentTimeMillis();
        try {
            //执行目标方法(切点方法中的某个方法)
            Object result = jp.proceed();
            long t2=System.currentTimeMillis();
            log.info("opertime:{}",t2-t1);
            doLogInfo(jp,t2-t1,null);
            return result;//目标业务方法的执行结果
        }catch(Throwable e){
            e.printStackTrace();
            long t2=System.currentTimeMillis();
            log.info("exception:{}",e.getMessage());
            doLogInfo(jp,t2-t1,e);
            throw e;
        }
    }

    private void doLogInfo(ProceedingJoinPoint jp,
                           long time,Throwable e)
            throws Exception {
        //1.获取用户行为日志
        //1.1 获取登录用户名(没做登录时，可以先给个固定值)
        String username="cgb";
        //1.2 获取 ip 地址
        String ip= "202.106.0.20";
        //1.3 获取操作名(operation)-@RequiredLog 注解中 value 属性的值
        //1.3.1 获取目标对象类型
        Class<?> targetCls=jp.getTarget().getClass();
        //1.3.2 获取目标方法
        MethodSignature ms=
                (MethodSignature) jp.getSignature();//方法签名
        Method targetMethod=targetCls.getMethod(
                ms.getName(),ms.getParameterTypes());
        //1.3.3 获取方法上 RequiredLog 注解
        RequiredLog annotation =
                targetMethod.getAnnotation(RequiredLog.class);
        //1.3.4 获取注解中定义操作名
        String operation=annotation.operation();
        //1.4 获取方法声明(类全名+方法名)
        String classMethodName=
                targetCls.getName()+"."+targetMethod.getName();
        //1.5 获取方法实际参数信息
        Object[]args=jp.getArgs();
        String params=new ObjectMapper().writeValueAsString(args);
        //2.封装用户行为日志
        NoticeLog sysLog=new NoticeLog();
        sysLog.setUsername(username);
        sysLog.setIp(ip);
        sysLog.setOperation(operation);
        sysLog.setMethod(classMethodName);
        sysLog.setParams(params);
        sysLog.setTime(time);
        if(e!=null) {
            sysLog.setStatus(0);
            sysLog.setError(e.getMessage());
        }
        //3.打印日志
        logService.insertLog(sysLog);
    }
}

